package com.myfoon.model.ejb;


import com.myfoon.model.cacheable.UserCache;
import com.myfoon.model.cacheable.UserSecurityCache;
import com.myfoon.model.entity.User;
import com.myfoon.model.entity.UserPi;
import com.myfoon.model.entryprocessor.FriendSelector;
import com.myfoon.model.exception.PasswordNotMatchException;
import com.myfoon.model.helper.CacheHelper;

import com.myfoon.model.helper.UserCacheHelper;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;
import com.tangosol.util.Filter;
import com.tangosol.util.filter.EqualsFilter;
import com.tangosol.util.filter.LimitFilter;

import java.sql.Timestamp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.ejb.EJBException;
import javax.ejb.Local;
import javax.ejb.Stateless;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;


@Stateless(name = "UserEJB", mappedName = "UserEJB")
@Local
public class UserEJBBean implements UserEJBLocal {
    @PersistenceContext(unitName = "MyfoonPU")
    EntityManager em;

    public UserEJBBean() {
    }

    public User persist(User user) {                        
        user.setCreated(new Timestamp(new Date().getTime()));        
        user.setPhotoNormal("/images/man.png");
        user.setPhotoSmall("/images/man.png");        
        user.setStatus((short)1);
        
        em.persist(user);
        
        NamedCache userCacheService = CacheFactory.getCache("User");        
        NamedCache userSecurityCacheService = CacheFactory.getCache("UserSecurity");        
        NamedCache inboxCacheService = CacheFactory.getCache("UserInbox");                
        NamedCache friendListCacheService = CacheFactory.getCache("FriendList");
        
        List<Long> friendList = new ArrayList<Long>();
        friendList.add(new Long(1));
        
        userCacheService.put(user.getId(), new UserCache(user));        
        userSecurityCacheService.put(user.getEmail(), new UserSecurityCache(user.getId(), user.getPasswd()));
        
        inboxCacheService.put(user.getId(), new ArrayList<Long>());        
        friendListCacheService.put(user.getId(), friendList);
        return user;
    }

    public User merge(User user) {
        return null;
    }

    public void remove(User user) {
    }
    
    public List<UserCache> getUsers(List<Long> userIDs) {        
        List<UserCache> userCaches = new ArrayList<UserCache>();
        Map userCacheMap = getUsersMap(userIDs);
        for (Iterator iter = userCacheMap.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry)iter.next();
            userCaches.add((UserCache)entry.getValue());            
        }
        
        Collections.sort(userCaches);
        return userCaches;
    }
    
    public Map getUsersMap(List<Long> userIDs) {        
        NamedCache userCacheService = CacheFactory.getCache("User");        
        Map userCacheMap = userCacheService.getAll(userIDs);
        
        for (Iterator iter = userCacheMap.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry)iter.next();
            UserCache uc = (UserCache)entry.getValue();
            if (userIDs.contains(uc.getId())) {
                userIDs.remove(uc.getId());
            }
        }
        
        if (userIDs.size() > 0) {
            Query q3 = em.createQuery("select u from User u left join fetch u.userPi where u.id in (:user_ids)");
            q3.setParameter("user_ids", userIDs);
            List<User> userList = q3.getResultList();
            
            Map NotExistingUserCacheMap = new HashMap();
            
            for (User u : userList) {                
                UserCache uc = new UserCache(u);
                
                userCacheMap.put(uc.getId(), uc);
                NotExistingUserCacheMap.put(uc.getId(), uc);                
            }
                
            userCacheService.putAll(NotExistingUserCacheMap);            
        }        
        
        return userCacheMap;        
    }    
    
    public User findByEmailAndPassword(String email, String passwd) {
        User user = null;        
        UserCacheHelper userCachehelper = new UserCacheHelper();
        
        try {
            user = userCachehelper.findByEmailAndPassword(email, passwd);            
        }   
        catch (PasswordNotMatchException e) {
            throw new PasswordNotMatchException();
        }
        
        if (user == null) {
            Query query = em.createNamedQuery("User.findByEmailAndPassword");
            query.setParameter("email", email);
            query.setParameter("passwd", passwd);    
            user = (User)query.getSingleResult();                        
            userCachehelper.put(user);
        }
        
        return user;        
    }            

    public List<Long> findFriendIDs(User user) {   
        NamedCache cache = CacheFactory.getCache("FriendList");                
        Object friendListObj = cache.get(user.getId());        
        
        List<Long> friendList = null;
        
        if (friendListObj == null) {
            Query query = em.createNamedQuery("User.findFriendIDs");
            query.setParameter("owner_id", user.getId());    
            friendList = query.getResultList();            
            cache.put(user.getId(), friendList);
            return friendList;
        }
        else {            
            return (List<Long>)friendListObj;
        }        
    }
    
    public Map<Long, List> findFriendINPs(User user, int first, int max) {        
        List<Long> friendList = this.findFriendIDs(user);                                
        
        Map userCacheMap = getUsersMap(friendList);
        List resultList = new ArrayList();        
        
        for (Iterator iter = userCacheMap.entrySet().iterator(); iter.hasNext();) {
            Map.Entry entry = (Map.Entry)iter.next();
            UserCache uc = (UserCache)entry.getValue();
            
            Object obj[] = new Object[3];
            obj[0] = uc.getId();
            obj[1] = uc.getFullname();
            obj[2] = uc.getPhotoSmall();
            resultList.add(obj);
        }
        
        Map<Long, List> result = new HashMap<Long, List>();        
        result.put(new Long(friendList.size()), resultList);
        return result;
    }

    public Object[] findRandomFriends(User user, int count) {
        NamedCache friendListCache = CacheFactory.getCache("FriendList");        
        Object resultObj = friendListCache.invoke(new Long(user.getId()), new FriendSelector(true, count));
                
        if (resultObj == null) {
            findFriendIDs(user);
            resultObj = friendListCache.invoke(new Long(user.getId()), new FriendSelector(true, count));                                                    
        }        
                        
        Object results[] = (Object[])resultObj;
        Integer size = (Integer)results[0];            
        
        List<Long> friendIDs = (List<Long>)results[1];
        
        List<UserCache> randomFriendList = getUsers(friendIDs);
        results[1] = randomFriendList;
        
        return results;
    }

    /*
     * Finds friends paged, Object[] array has 2 elements. 
     * - 1st element store number of friends (count)
     * - 2nd element store list of UserCaches
     */
    public Object[] findPagedFriends(User user, int start, int max) {
        NamedCache friendListCache = CacheFactory.getCache("FriendList");        
        Object resultObj = friendListCache.invoke(new Long(user.getId()), new FriendSelector(start, max));
                
        if (resultObj == null) {
            findFriendIDs(user);
            resultObj = friendListCache.invoke(new Long(user.getId()), new FriendSelector(start, max));                                            
        }        
                
        Object results[] = (Object[])resultObj;
        Integer size = (Integer)results[0];        
        List<Long> friendIDs = (List<Long>)results[1];
                                        
        List<UserCache> randomFriendList = getUsers(friendIDs);
        results[1] = randomFriendList;
        
        return results;
    }    
}
